package grpcserver

import (
	"context"
	"crypto/tls"
	"fmt"
	utilNet "gitee.com/vrv_media/go-micro-framework/pkg/common/util/net"
	"gitee.com/vrv_media/go-micro-framework/registry"
	"gitee.com/vrv_media/go-micro-framework/registry/balance/round_robin"
	"gitee.com/vrv_media/go-micro-framework/registry/discovery/native_config"
	"gitee.com/vrv_media/go-micro-framework/registry/resolver"
	clientinterceptors "gitee.com/vrv_media/go-micro-framework/server/grpcserver/interceptors/client_interceptors"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	grpcinsecure "google.golang.org/grpc/credentials/insecure"
	"time"
)

type Option func(o *ClientOptions)
type GenerateClientFunc[T any] func(grpc.ClientConnInterface) T

type ClientOptions struct {
	endpoint string
	tlsConf  *tls.Config
	timeout  time.Duration
	// discovery 接口
	discovery registry.Discovery // 服务发现
	resolver  registry.Resolver  // 服务解析
	balance   registry.Balancer  // 负载均衡

	unaryInterceptors  []grpc.UnaryClientInterceptor  // 一元拦截器
	streamInterceptors []grpc.StreamClientInterceptor // 流式拦截器
	rpcOpts            []grpc.DialOption
	serverName         string
	insecure           bool // 是否是安全连接
	enableTracing      bool
	enableMetrics      bool
}

func NewDefaultClientOptions(endpoint, serverName string) *ClientOptions {
	return &ClientOptions{
		endpoint:           endpoint,
		timeout:            time.Second * 5,
		unaryInterceptors:  make([]grpc.UnaryClientInterceptor, 0),  // 一元拦截器
		streamInterceptors: make([]grpc.StreamClientInterceptor, 0), // 流式拦截器
		rpcOpts:            make([]grpc.DialOption, 0),
		serverName:         serverName,
		insecure:           true,
		enableTracing:      false,
		enableMetrics:      false,
	}
}

type Client[T any] struct {
	options *ClientOptions
	// 最终生成的rpc的Opts，会将options里面的拦截器最终生成为这grpc的拦截器
	rpcOpts []grpc.DialOption
	// 返回具体类型的方法
	generateClientFunc GenerateClientFunc[T]
}

func NewDefaultClient[T any](endpoint, serverName string, generateClientFunc GenerateClientFunc[T], opts ...Option) *Client[T] {
	options := NewDefaultClientOptions(endpoint, serverName)
	// 适配传入的参数
	if len(opts) > 0 {
		for _, o := range opts {
			o(options)
		}
	}
	if options.discovery == nil {
		options.discovery = native_config.NewNativeConfigDiscovery(endpoint)
	}
	if options.balance == nil {
		options.balance = round_robin.NewRoundRobinBalancer()
	}
	if options.resolver == nil {
		options.resolver = resolver.NewResolve(serverName, options.discovery, options.balance)
	}
	client := &Client[T]{
		options:            options,
		rpcOpts:            make([]grpc.DialOption, 0),
		generateClientFunc: generateClientFunc,
	}
	err := client.buildGrpcOpts()
	if err != nil {
		panic(err)
	}
	return client
}

func (c *Client[T]) buildGrpcOpts() error {

	// 客户端默认拦截器
	unaryInterceptors := []grpc.UnaryClientInterceptor{
		clientinterceptors.TimeoutInterceptor(c.options.timeout), // 超时拦截器
	}
	// 开启 链路追踪
	if c.options.enableTracing {
		unaryInterceptors = append(unaryInterceptors, clientinterceptors.UnaryTracingInterceptor)
	}

	// 开启 普罗米修斯
	if c.options.enableMetrics {
		unaryInterceptors = append(unaryInterceptors, clientinterceptors.UnaryPrometheusInterceptor)
	}
	if len(c.options.unaryInterceptors) > 0 {
		unaryInterceptors = append(unaryInterceptors, c.options.unaryInterceptors...)
	}

	streamInterceptors := []grpc.StreamClientInterceptor{}

	if len(c.options.streamInterceptors) > 0 {
		streamInterceptors = append(streamInterceptors, c.options.streamInterceptors...)
	}
	// 生成grpcOpts
	grpcOpts := []grpc.DialOption{
		grpc.WithChainUnaryInterceptor(unaryInterceptors...),
		grpc.WithChainStreamInterceptor(streamInterceptors...),
	}

	if c.options.insecure {
		grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(grpcinsecure.NewCredentials()))
	}
	if c.options.tlsConf != nil {
		grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(c.options.tlsConf)))
	}
	if len(c.options.rpcOpts) > 0 {
		grpcOpts = append(grpcOpts, c.options.rpcOpts...)
	}
	c.rpcOpts = grpcOpts
	return nil
}

func (c *Client[T]) RebuildGrpcOpts() error {
	return c.buildGrpcOpts()
}

// NextClient 获得下一个client,通过这个方法，会获得最终的参数
func (c *Client[T]) NextClient(ctx context.Context) (t T, err error) {
	// 从服务发现中获得下一个节点信息
	selected, err := c.options.resolver.Next(ctx)
	if err != nil {
		return
	}
	if selected.Instance() != nil {
		if value, ok := selected.Instance().(grpc.ClientConnInterface); ok {
			t = c.generateClientFunc(value)
			return
		}
	}
	conn, err := c.dialNode(ctx, selected)
	if err != nil {
		return
	}
	selected.SetInstance(conn)
	t = c.generateClientFunc(conn)
	return
}

func (c *Client[T]) dialNode(ctx context.Context, node registry.Node) (grpc.ClientConnInterface, error) {
	host, port, err := utilNet.ParseHostAndPort(node.Address())
	if err != nil {
		return nil, err
	}
	conn, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%s", host, port), c.rpcOpts...)
	return conn, nil
}

// WithClientTLSConfig with tls config.
func WithClientTLSConfig(c *tls.Config) Option {
	return func(o *ClientOptions) {
		o.tlsConf = c
	}
}

// WithClientEndpoint 设置地址
func WithClientEndpoint(endpoint string) Option {
	return func(o *ClientOptions) {
		o.endpoint = endpoint
	}
}

// WithClientTimeout 设置超时时间
func WithClientTimeout(timeout time.Duration) Option {
	return func(o *ClientOptions) {
		o.timeout = timeout
	}
}

// WithClientDiscovery 设置服务发现
func WithClientDiscovery(d registry.Discovery) Option {
	return func(o *ClientOptions) {
		o.discovery = d
	}
}

// WithClientBalancer 设置负载均衡
func WithClientBalancer(b registry.Balancer) Option {
	return func(o *ClientOptions) {
		o.balance = b
	}
}

// WithClientResolver 设置服务解析
func WithClientResolver(r registry.Resolver) Option {
	return func(o *ClientOptions) {
		o.resolver = r
	}
}

// WithClientUnaryInterceptor 设置 一元拦截器
func WithClientUnaryInterceptor(in ...grpc.UnaryClientInterceptor) Option {
	return func(o *ClientOptions) {
		o.unaryInterceptors = in
	}
}

// WithClientStreamInterceptor 设置 stream 拦截器
func WithClientStreamInterceptor(in ...grpc.StreamClientInterceptor) Option {
	return func(o *ClientOptions) {
		o.streamInterceptors = in
	}
}

// WithClientDialOption 设置 grpc 的 dial 选项
func WithClientDialOption(opts ...grpc.DialOption) Option {
	return func(o *ClientOptions) {
		o.rpcOpts = opts
	}
}

// WithClientEnableTracing 设置是否开启链路追踪
func WithClientEnableTracing(enable bool) Option {
	return func(o *ClientOptions) {
		o.enableTracing = enable
	}
}

// WithClientInsecure 设置是否安全连接
func WithClientInsecure(enable bool) Option {
	return func(o *ClientOptions) {
		o.insecure = enable
	}
}

// WithClientEnableMetrics 设置是否开启 普罗米修斯 监控
func WithClientEnableMetrics(enable bool) Option {
	return func(o *ClientOptions) {
		o.enableMetrics = enable
	}
}
